﻿/*
 * RssFeedTracer
 *
 * Used to trace all subscribed rss feed and auto-update by the time interval it provides
 *
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Snowdreamist.Threading;

namespace SNSpirit.Domain.Rss
{
    internal class RssFeedTracer
    {
        public RssFeedTracer(IRssServiceConnector connector)
        {
            if (connector == null)
                throw new ArgumentNullException("connector");
            this._connector = connector;
            this._wrapperList = new List<RssTracerWrapper>();
            // Refresh interval is 30s, fixed at M1
            BackgroundJob.Run(new Action(JobCallback), "Rss trace job", TimeSpan.FromSeconds(30));
        }

        public void Trace(RssFeed rssFeed)
        {
            lock (this)
            {
                var r = this._wrapperList.Where(p => p.Feed == rssFeed);

                if (r.Count() == 0)
                {
                    // add
                    this._wrapperList.Add(new RssTracerWrapper(rssFeed));
                }
                else
                {
                    // update
                    var wrapper = r.First();
                    wrapper.Feed = rssFeed;
                    wrapper.MoveNextTriggerTime();
                }
            }
        }

        public void RemoveTracing(RssFeed rssFeed)
        {
            lock (this)
            {
                var r = this._wrapperList.Where(p => p.Feed == rssFeed);

                if (r.Count() != 0)
                {
                    // remove
                    this._wrapperList.Remove(r.First());
                }
            }
        }

        public event EventHandler<RssTraceErrorOccurredEventArgs> RssTraceErrorOccurred;

        protected virtual void OnRssTraceErrorOccurred(RssFeed feed, Exception error)
        {
            if (this.RssTraceErrorOccurred != null)
                this.RssTraceErrorOccurred(this, new RssTraceErrorOccurredEventArgs(feed, error));
        }

        #region private

        private IRssServiceConnector _connector;

        private List<RssTracerWrapper> _wrapperList;

        private void JobCallback()
        {
            RssTracerWrapper[] wrappers;

            lock (this)
            {
                DateTime now = RssTracerWrapper.Now;
                wrappers = this._wrapperList.Where(p => !p.IsUpdating && p.TriggerTime < now).Select((p) =>
                    {
                        p.IsUpdating = true;
                        return p;
                    }).ToArray();
            }

            // update
            foreach (var wrapper in wrappers)
                BackgroundJob.Run(new Action<RssTracerWrapper>(this.Update), "Update Rssfeed:" + wrapper.Feed.UpdateUri, wrapper);
        }

        private void Update(RssTracerWrapper wrapper)
        {
            if (wrapper == null)
                return;

            try
            {
                var r = this._connector.Update(wrapper.Feed);
                if (r)
                    wrapper.Feed.Channel.NotifyUpdated();
            }
            catch (Exception error)
            {
                this.OnRssTraceErrorOccurred(wrapper.Feed, error);
            }
            finally
            {
                wrapper.IsUpdating = false;
                wrapper.MoveNextTriggerTime();
            }
        }

        #endregion private
    }
}